package im.yangqiang.android.ui.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;


import java.util.ArrayList;
import java.util.List;

import im.yangqiang.android.ui.R;

/**
 * 奖励的自定义控件，可以实现一条线上几个寻宝的功能
 *
 * Created by Carlton on 2016/4/20.
 */
public class RewardView extends View
{
    /**
     * 是否调试
     */
    private final boolean DEBUG = false;
    /**
     * 最高的点
     */
    private PointF mHighestPoint = new PointF();
    /**
     * 最低的点
     */
    private PointF mLowestPoint = new PointF();
    /**
     * 第一个点
     */
    private PointF mFirstPoint = new PointF();
    /**
     * 最后一个点
     */
    private PointF mLastPoint = new PointF();
    /**
     * 主线的宽度
     */
    private float mLineWidth;
    private int mLineColor;
    private float mCanvasPadding;

    public RewardView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    public RewardView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public RewardView(Context context)
    {
        super(context);
        init(context, null, 0);
    }

    private void init(Context context, AttributeSet attrs, int defStyle)
    {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RewardView, defStyle, 0);
        mLineWidth = typedArray.getDimension(R.styleable.RewardView_line_width, 10);
        mCanvasPadding = typedArray.getDimension(R.styleable.RewardView_padding, 10);
        mLineColor = typedArray.getColor(R.styleable.RewardView_line_color, Color.argb(100, 255, 0, 255));
        typedArray.recycle();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setColor(mLineColor);
        if (isInEditMode())
        {
            demo();
        }
        setWillNotDraw(false);
    }

    private void setData()
    {
        mHighestPoint = highestPoint();
        mLowestPoint = lowestPoint();
        mFirstPoint = firstPoint();
        mLastPoint = lastPoint();
    }

    private void demo()
    {
        ArrayList<PointF> list = new ArrayList<>();
        list.add(new PointF(25.5f, 305.89f));
        list.add(new PointF(95.89f, 237.51f));
        list.add(new PointF(186.76f, 305.89f));
        list.add(new PointF(277.64f, 374.26f));
        list.add(new PointF(347.5f, 305.89f));
        addPoints(list);
    }

    private ArrayList<PointF> mSrcPoint = new ArrayList<>();
    private ArrayList<PointF> mConvertPoint = new ArrayList<>();

    /**
     * 添加点
     *
     * @param point 被添加的点
     */
    public void addPoint(PointF point)
    {
        mSrcPoint.add(point);
        setData();
    }

    /**
     * 添加点
     *
     * @param list 被添加的点集合
     */
    public void addPoints(List<PointF> list)
    {
        mSrcPoint.addAll(list);
        setData();
    }

    /**
     * 清空元数据点
     */
    public void clearPoints()
    {
        mSrcPoint.clear();
    }

    /**
     * 返回第一个点
     *
     * @return
     */
    public PointF firstPoint()
    {
        return (null != mSrcPoint && !mSrcPoint.isEmpty()) ? mSrcPoint.get(0) : null;
    }

    /**
     * 返回最后一个点
     *
     * @return
     */
    public PointF lastPoint()
    {
        return (null != mSrcPoint && !mSrcPoint.isEmpty()) ? mSrcPoint.get(mSrcPoint.size() - 1) : null;
    }

    /**
     * 返回最高点,屏幕顶部为最高
     *
     * @return 最高点
     */
    public PointF highestPoint()
    {
        PointF point = new PointF(Integer.MAX_VALUE, Integer.MAX_VALUE);
        for (PointF p : mSrcPoint)
        {
            if (p.y < point.y)
            {
                point = p;
            }
        }
        return point;
    }

    /**
     * 返回最低点，屏幕顶部为最高
     *
     * @return
     */
    public PointF lowestPoint()
    {
        PointF point = new PointF(Integer.MIN_VALUE, Integer.MIN_VALUE);
        for (PointF p : mSrcPoint)
        {
            if (p.y > point.y)
            {
                point = p;
            }
        }
        return point;
    }

    /**
     * 绘制的Canvas的padding，同时作用于左右
     *
     * @return
     */
    public float getCanvasPadding()
    {
        return mCanvasPadding;
    }

    /**
     * 转换坐标点，让新的点可以适配屏幕宽高
     *
     * @param point 需要转换的点
     * @return 转换后的可以适配屏幕的点
     */
    private PointF convertPoint(PointF point)
    {
        final float space = getPaddingLeft() + getPaddingRight() + mCanvasPadding;
        final int width = (int) (getMeasuredWidth() - space);
        final float xb = width / (mLastPoint.x - mFirstPoint.x);
        final float centerOffset = (getMeasuredHeight() - (mLowestPoint.y - mHighestPoint.y) * xb) * 0.5f;
        return new PointF(xb * (point.x - mFirstPoint.x) + space / 2, (point.y - mHighestPoint.y) * xb + centerOffset);
    }

    /**
     * 返回贝塞尔曲线上某一点的切线角度
     *
     * @param x
     * @return
     */
    public double getAngel(float x)
    {
        double y = getQuadY(x);
        float x1 = x + 3;
        double y1 = getQuadY(x1);
        return Math.toDegrees(Math.atan((y1 - y) / (x1 - x)));
    }

    /**
     * 返回贝塞尔曲线上某一点的Y
     *
     * @param x
     * @return
     */
    public double getQuadY(float x)
    {
        return getKeyPointY(getKeyPointParams(x), x);
    }

    /**
     * 通过贝塞尔曲线的x值获取对应曲线上的y值
     *
     * @param x  需要计算y值对应点的x值
     * @param p0 起点
     * @param p1 控制点
     * @param p2 终点
     * @return
     */
    private double getYWithXt(double x, PointF p0, PointF p1, PointF p2)
    {
        double b = 2 * p1.x - 2 * p0.x;
        double a = p0.x - 2 * p1.x + p2.x;
        double c = p0.x - x;
        double t = (-b + Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
        if (t < 0 || t > 1)
        {
            t = (-b - Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
        }
        return (1 - t) * (1 - t) * p0.y + 2 * t * (1 - t) * p1.y + t * t * p2.y;
    }

    private float getKeyPointY(PointF[] params, float i)
    {
        if (null == params || params.length != 3)
        {
            return 0;
        }
        return (float) getYWithXt(i, params[0], params[1], params[2]);
    }

    /**
     * 返回关键点的位置
     *
     * @param pointCount 关键点的个数
     * @return 关键点列表
     */
    public ArrayList<PointF> getKeyPoints(int pointCount)
    {
        ArrayList<PointF> list = new ArrayList<>();
        if (pointCount == 1)
        {
            list.add(mConvertPoint.get(0));
            return list;
        }
        // 点去拆分出来的每一段的长度（x值)
        int lineLength = (getWidth() - getPaddingLeft() - getPaddingRight()) / (pointCount - 1);

        for (int i = 1; i < pointCount - 1; ++i)
        {
            list.add(new PointF(lineLength * i, getKeyPointY(getKeyPointParams(lineLength * i), lineLength * i)));
        }

        if (mConvertPoint.size() > 2)
        {
            list.add(mConvertPoint.get(mConvertPoint.size() - 1));
            list.add(0, mConvertPoint.get(0));
        }

        return fineTuningPoints(list);
    }

    /**
     * 微调数据(针对项目特殊处理，其他项目请删除改方法）
     *
     * @param list
     * @return
     * @deprecated
     */
    public ArrayList<PointF> fineTuningPoints(ArrayList<PointF> list)
    {
        if (list.size() != 8 || list.size() <= mConvertPoint.size() - 3)
        {
            return list;
        }
//        list.get(1).x += UiUtils.dip2px(getContext(), 10);
//        list.get(1).y += UiUtils.dip2px(getContext(), 7);
//        list.get(2).x += UiUtils.dip2px(getContext(), 6);
//        list.get(2).y -= UiUtils.dip2px(getContext(), 4);
//        list.get(mConvertPoint.size() - 3).x -= UiUtils.dip2px(getContext(), 3);
//        list.get(mConvertPoint.size() - 3).y += UiUtils.dip2px(getContext(), 4);
        return list;
    }

    /**
     * 返回关键点的计算参数
     *
     * @param x 关键点
     * @return 一个数组包含了x所在的贝塞尔曲线的 起点x， 控制点x， 终点x
     */
    private PointF[] getKeyPointParams(float x)
    {
        PointF[] result = null;
        for (int i = 2; i < mConvertPoint.size(); i = i + 2)
        {
            if (x < mConvertPoint.get(i).x)
            {
                result = new PointF[3];
                result[0] = mConvertPoint.get(i - 2);
                result[1] = mConvertPoint.get(i - 1);
                result[2] = mConvertPoint.get(i);
                break;
            }
        }
        return result;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width = widthSize;
        int height = heightSize - paddingTop - paddingBottom;

        if (widthMode == MeasureSpec.EXACTLY)
        {
            width = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST)
        {
            width = widthSize - paddingLeft - paddingRight;
        }
        final float minHeight = ((mLowestPoint.y - mHighestPoint.y) * (getMeasuredWidth() / (mLastPoint.x - mFirstPoint.x))) - paddingTop - paddingBottom;
        setMeasuredDimension(width, (int) Math.max(height, minHeight));

        mConvertPoint.clear();
        for (PointF p : mSrcPoint)
        {
            mConvertPoint.add(convertPoint(p));
        }
    }

    private Path path = new Path();
    private Paint mPaint = new Paint();

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        if (mConvertPoint.isEmpty())
        {
            return;
        }
        path.reset();
        PointF first = mConvertPoint.get(0);
        path.moveTo(first.x, first.y);
        for (int i = 1; i < mConvertPoint.size(); i = i + 2)
        {
            PointF controlPoint = mConvertPoint.get(i);
            PointF endPoint = mConvertPoint.get((i + 1) >= mConvertPoint.size() ? i : (i + 1));
            path.quadTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
        }
        mPaint.setStrokeWidth(mLineWidth);
        canvas.drawPath(path, mPaint);
        if (DEBUG)
        {
            Paint paint = new Paint();
            for (PointF point : mConvertPoint)
            {
                paint.setColor(Color.RED);
//                drawOval(canvas, point, paint);
            }
            for (PointF p : getKeyPoints(((ViewGroup) getParent()).getChildCount() - 1))
            {
                paint.setColor(Color.BLUE);
                drawOval(canvas, p, paint);
            }
            paint.setStrokeWidth(10);
            paint.setColor(Color.BLACK);
//            canvas.drawLine(0, getMeasuredHeight() / 2, getMeasuredWidth(), getMeasuredHeight() / 2, paint);
        }
    }

    /**
     * 绘制圆形
     *
     * @param canvas 画布
     * @param center 中点
     */
    private void drawOval(Canvas canvas, PointF center, Paint paint)
    {
        final int r = 15;
        canvas.drawRect(center.x - r, center.y - r, center.x + r, center.y + r, paint);
    }
}